﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//    NotificationChannelListTest.cs
//
// Abstract:
//    
//    Unit tests for a list of channels.
//
//--

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;

namespace Microsoft.EnhancedReminders.Common.UnitTests
{    
    /// <summary>
    ///This is a test class for NotificationChannelListTest and is intended
    ///to contain all NotificationChannelListTest Unit Tests
    ///</summary>
    [TestClass()]
    public class NotificationChannelListTest
    {
        private TestContext testContextInstance;

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return testContextInstance;
            }
            set
            {
                testContextInstance = value;
            }
        }

        /// <summary>
        /// Test constructor correctly initializes to the given list of channels.
        ///</summary>
        [TestMethod()]
        public void TestConstructor()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/true);

            NotificationChannelList referenceList = new NotificationChannelListAbsoluteTime(masterChannels);

            // Verify channels list exactly matches masterChannels.
            Assert.AreEqual(masterChannels.Count, referenceList.Channels.Count, "Count");
            for (int i = 0; i < masterChannels.Count; i++)
            {
                Assert.IsTrue(CompareMasterToReference(masterChannels[i], referenceList.Channels[i], masterChannels[i].IsEnabled), "Reference channel " + i);
            }
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verifies that an empty reference list becomes populated
        /// with the master list but with IsEnabled set to false for each channel.
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList()
        {
            NotificationChannelList referenceList = new NotificationChannelListLeadTime(new List<NotificationChannel>());

            Assert.IsNotNull(referenceList.Channels, "referenceList.Channels");
            Assert.AreEqual(0, referenceList.Channels.Count, "referenceList.Channels.Count");

            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/true);

            // Sync the reference list up to the master list without appending.
            referenceList.SyncToMasterChannelsList(masterChannels, false, /*appendUnseenChannels*/false);
            Assert.AreEqual(0, referenceList.Channels.Count, "referenceList.Channels.Count after syncing without appending");

            // Sync the reference list up to the master list.
            referenceList.SyncToMasterChannelsList(masterChannels, false, /*appendUnseenChannels*/true);

            // Verify the updated list matches the master list.
            Assert.AreEqual(masterChannels.Count, referenceList.Channels.Count, "Count");
            for (int i = 0; i < masterChannels.Count; i++)
            {
                Assert.IsTrue(CompareMasterToReference(masterChannels[i], referenceList.Channels[i], false), "Reference channel " + i);
            }
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verify order of reference channels is preserved after sync'ing to the master list.
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList2()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/true);

            NotificationChannelList reference = new NotificationChannelListAbsoluteTime(masterChannels);

            // Change order of reference channels and toggle IsEnabled on some channels.
            reference.Channels.Reverse();
            reference.Channels[2].IsEnabled = !reference.Channels[2].IsEnabled;
            reference.Channels[4].IsEnabled = !reference.Channels[4].IsEnabled;
            reference.Channels[5].IsEnabled = !reference.Channels[5].IsEnabled;

            Assert.AreEqual(masterChannels.Count, reference.Channels.Count, "Count");

            reference.SyncToMasterChannelsList(masterChannels, false, /*appendUnseenChannels*/true); // Sync to the master list (which hasn't changed).

            Assert.IsTrue(CompareMasterToReference(masterChannels[6], reference.Channels[0], masterChannels[6].IsEnabled), "Reference channel 0");
            Assert.IsTrue(CompareMasterToReference(masterChannels[5], reference.Channels[1], masterChannels[5].IsEnabled), "Reference channel 1");
            Assert.IsTrue(CompareMasterToReference(masterChannels[4], reference.Channels[2], !masterChannels[4].IsEnabled), "Reference channel 2");
            Assert.IsTrue(CompareMasterToReference(masterChannels[3], reference.Channels[3], masterChannels[3].IsEnabled), "Reference channel 3");
            Assert.IsTrue(CompareMasterToReference(masterChannels[2], reference.Channels[4], !masterChannels[2].IsEnabled), "Reference channel 4");
            Assert.IsTrue(CompareMasterToReference(masterChannels[1], reference.Channels[5], !masterChannels[1].IsEnabled), "Reference channel 5");
            Assert.IsTrue(CompareMasterToReference(masterChannels[0], reference.Channels[6], masterChannels[0].IsEnabled), "Reference channel 6");
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verifies reference list containing channels no longer in the master list.
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList3()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/true);

            // Initialize reference channels and toggle some IsEnabled values (to simulate usage).
            NotificationChannelList reference = new NotificationChannelListAbsoluteTime(masterChannels);
            reference.Channels[3].IsEnabled = !reference.Channels[3].IsEnabled;
            reference.Channels[5].IsEnabled = !reference.Channels[5].IsEnabled;
            reference.Channels[6].IsEnabled = !reference.Channels[6].IsEnabled;

            // Delete the custom channels from the master list.
            masterChannels.RemoveAt(2);
            masterChannels.RemoveAt(masterChannels.Count - 1);

            reference.SyncToMasterChannelsList(masterChannels, false, /*appendUnseenChannels*/true); // Sync to the updated master channels list.

            Assert.AreEqual(5, masterChannels.Count, "masterChannels.Count");
            Assert.AreEqual(masterChannels.Count, reference.Channels.Count, "referenceChannels.Count");

            Assert.IsTrue(CompareMasterToReference(masterChannels[0], reference.Channels[0], masterChannels[0].IsEnabled), "Reference channel 0");
            Assert.IsTrue(CompareMasterToReference(masterChannels[1], reference.Channels[1], masterChannels[1].IsEnabled), "Reference channel 1");
            Assert.IsTrue(CompareMasterToReference(masterChannels[2], reference.Channels[2], !masterChannels[2].IsEnabled), "Reference channel 2");
            Assert.IsTrue(CompareMasterToReference(masterChannels[3], reference.Channels[3], masterChannels[3].IsEnabled), "Reference channel 3");
            Assert.IsTrue(CompareMasterToReference(masterChannels[4], reference.Channels[4], !masterChannels[4].IsEnabled), "Reference channel 4");
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verifies master list containing channels not in the reference 
        /// list appends if "appendUnseenChannels" is true.
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList4()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/false);

            NotificationChannelList reference = new NotificationChannelListAbsoluteTime(masterChannels);

            // Shuffle reference channels slightly (swapping 0 and 4) and change IsEnabled on some channels.
            reference.Channels[0].IsEnabled = !reference.Channels[0].IsEnabled;
            reference.Channels[2].IsEnabled = !reference.Channels[2].IsEnabled;
            reference.Channels[3].IsEnabled = !reference.Channels[3].IsEnabled;
            NotificationChannel chan = reference.Channels[4];
            reference.Channels[4] = reference.Channels[0];
            reference.Channels[0] = chan;

            // Add some new custom channels to the master list.
            masterChannels.Add(new NotificationCustomChannel(NotificationChannelType.SMS, "channel6", "My SMS channel"));
            masterChannels.Add(new NotificationCustomChannel(NotificationChannelType.Communicator, "channel7", "My IM channel"));           
            
            reference.SyncToMasterChannelsList(masterChannels, false, /*appendUnseenChannels*/true); // Sync up to the updated master channel list.

            Assert.AreEqual(7, masterChannels.Count, "masterChannels.Count");
            Assert.AreEqual(masterChannels.Count, reference.Channels.Count, "referenceChannels.Count");

            // Verify reference channel order preserved and 2 new master channels have been appended to the end (with IsEnabled->false).
            Assert.IsTrue(CompareMasterToReference(masterChannels[4], reference.Channels[0], masterChannels[4].IsEnabled), "Reference channel 0");
            Assert.IsTrue(CompareMasterToReference(masterChannels[1], reference.Channels[1], masterChannels[1].IsEnabled), "Reference channel 1");
            Assert.IsTrue(CompareMasterToReference(masterChannels[2], reference.Channels[2], !masterChannels[2].IsEnabled), "Reference channel 2");
            Assert.IsTrue(CompareMasterToReference(masterChannels[3], reference.Channels[3], !masterChannels[3].IsEnabled), "Reference channel 3");
            Assert.IsTrue(CompareMasterToReference(masterChannels[0], reference.Channels[4], !masterChannels[0].IsEnabled), "Reference channel 4");
            Assert.IsTrue(CompareMasterToReference(masterChannels[5], reference.Channels[5], false), "Reference channel 5");
            Assert.IsTrue(CompareMasterToReference(masterChannels[6], reference.Channels[6], false), "Reference channel 6");
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verifies master list containing channels not in the reference 
        /// list does not append the additional channels if "appendUnseenChannels" is false.
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList5()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/false);

            NotificationChannelList reference = new NotificationChannelListAbsoluteTime(masterChannels);

            // Shuffle reference channels slightly (swapping 0 and 4) and change IsEnabled on some channels.
            reference.Channels[0].IsEnabled = !reference.Channels[0].IsEnabled;
            reference.Channels[2].IsEnabled = !reference.Channels[2].IsEnabled;
            reference.Channels[3].IsEnabled = !reference.Channels[3].IsEnabled;
            NotificationChannel chan = reference.Channels[4];
            reference.Channels[4] = reference.Channels[0];
            reference.Channels[0] = chan;

            // Add some new custom channels to the master list.
            masterChannels.Add(new NotificationCustomChannel(NotificationChannelType.SMS, "channel6", "My SMS channel"));
            masterChannels.Add(new NotificationCustomChannel(NotificationChannelType.Communicator, "channel7", "My IM channel"));

            reference.SyncToMasterChannelsList(masterChannels, false, /*appendUnseenChannels*/false); // Sync up to the updated master channel list.

            Assert.AreEqual(7, masterChannels.Count, "masterChannels.Count");
            Assert.AreEqual(5, reference.Channels.Count, "referenceChannels.Count");

            // Verify reference channel order preserved and 2 new master channels have been appended to the end.
            Assert.IsTrue(CompareMasterToReference(masterChannels[4], reference.Channels[0], masterChannels[4].IsEnabled), "Reference channel 0");
            Assert.IsTrue(CompareMasterToReference(masterChannels[1], reference.Channels[1], masterChannels[1].IsEnabled), "Reference channel 1");
            Assert.IsTrue(CompareMasterToReference(masterChannels[2], reference.Channels[2], !masterChannels[2].IsEnabled), "Reference channel 2");
            Assert.IsTrue(CompareMasterToReference(masterChannels[3], reference.Channels[3], !masterChannels[3].IsEnabled), "Reference channel 3");
            Assert.IsTrue(CompareMasterToReference(masterChannels[0], reference.Channels[4], !masterChannels[0].IsEnabled), "Reference channel 4");
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verifies that when syncing join now channels, an SMS channel
        /// does not appear on the output list.
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList6()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/true);
            NotificationChannelList reference = new NotificationChannelListAbsoluteTime(masterChannels);

            masterChannels.Add(new NotificationCustomChannel(NotificationChannelType.SMS, "New SMS", "1234@contoso.com"));

            reference.SyncToMasterChannelsList(masterChannels, true, true);

            Assert.AreNotEqual(masterChannels.Count, reference.Channels.Count, "Master Channel list and Reference Channel list should not have same number of channels.");
            Assert.IsTrue(reference.Channels.Count == masterChannels.Count - 2, "Reference channel list should contain one less channel than master list.");

            foreach (NotificationChannel channel in reference.Channels)
            {
                Assert.IsTrue(channel.AllowsJoinNow, "All channels in reference list should allow Join Now");
            }
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verifies the content fields of a reference channel are updated with 
        /// the contents of the corresponding master channel but preserves the reference channel IsEnabled property.
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList7()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/true);

            NotificationChannelList reference = new NotificationChannelListAbsoluteTime(masterChannels);

            Assert.AreEqual(masterChannels[0], reference.Channels[0], "Master and reference list should have the same first channel");

            // Toggle the IsEnabled flag on the reference channel.
            bool masterIsEnabled = masterChannels[0].IsEnabled;
            reference.Channels[0].IsEnabled = !masterIsEnabled;

            // Update the user value of one of the master channels and re-sync to the updated master list.
            masterChannels[0].UserDefinedValue = "My updated channel";
            reference.SyncToMasterChannelsList(masterChannels, false, true);

            Assert.AreEqual(masterChannels.Count, reference.Channels.Count, "Master Channel list and Reference Channel list should not have same number of channels.");
            Assert.AreEqual(!masterIsEnabled, reference.Channels[0].IsEnabled, "reference.Channels[0].IsEnabled");

            // Set reference.Channels[0].IsEnabled back to the master value so we can compare both channels.
            reference.Channels[0].IsEnabled = masterIsEnabled;

            // Channels should now be equal if data fields were copied from the master.
            Assert.AreEqual(masterChannels[0], reference.Channels[0], "Reference channel should be equal to master channel");
        }

        /// <summary>
        /// Test SyncToMasterChannelsList().  Verify that empty channels are removed (so they won't appear in the read-only channels UI).
        ///</summary>
        [TestMethod()]
        public void TestSyncToMasterChannelsList8()
        {
            List<NotificationChannel> masterChannels = CreateMasterChannelsList(/*createCustomChannels*/true);

            NotificationChannel homePhone = masterChannels[4];
            Assert.AreEqual("Home Phone", homePhone.UserDefinedName, "Expecting OCS home phone channel");
            homePhone.UserDefinedValue = null;

            NotificationChannelList reference = new NotificationChannelListAbsoluteTime(new List<NotificationChannel>());

            // Appending unseen channels should not include the empty channel.
            reference.SyncToMasterChannelsList(masterChannels, false, true);

            Assert.AreEqual(masterChannels.Count - 1, reference.Channels.Count, "Expecting masterChannels.Count -1");

            foreach (NotificationChannel channel in reference.Channels)
            {
                Assert.IsFalse(channel.Equals(homePhone), "Empty channels should have been removed");
            }

            // Now update the "Other Phone" channel to null and test that re-syncing removes this channel.
            NotificationChannel otherPhone = masterChannels[5];
            Assert.AreEqual("Other Phone", otherPhone.UserDefinedName, "Expecting OCS other phone channel");
            otherPhone.UserDefinedValue = null;

            reference.SyncToMasterChannelsList(masterChannels, false, true);

            Assert.AreEqual(masterChannels.Count - 2, reference.Channels.Count, "Expecting masterChannels.Count - 1");

            foreach (NotificationChannel channel in reference.Channels)
            {
                Assert.IsFalse(channel.Equals(homePhone) || channel.Equals(otherPhone), "Home and other phone channels should have been removed");
            }
        }

        /// <summary>
        /// Helper method to create a default list of channels for the master list.  Some of the channels are
        /// not enabled to simulate real usage.
        /// </summary>
        /// <param name="includeCustomChannels">Insert custom channels at arbitrary points in the list.</param>
        private static List<NotificationChannel> CreateMasterChannelsList(bool includeCustomChannels)
        {
            List<NotificationChannel> masterChannels = new List<NotificationChannel>();
            NotificationChannel channel = null;

            // Communicator channel.
            masterChannels.Add(new NotificationChannel(NotificationChannel.CommunicatorId, "Communicator", "john@contoso.com"));

            // Mobile phone.
            masterChannels.Add(new NotificationChannel(NotificationChannel.MobilePhoneId, "Mobile Phone", "123456789"));

            // Custom SMS channel (not enabled).
            if (includeCustomChannels)
            {
                channel = new NotificationCustomChannel(NotificationChannelType.SMS, "My SMS Channel", "1234@contoso.com");
                channel.IsEnabled = false;
                masterChannels.Add(channel);
            }           
            
            // Work phone.
            masterChannels.Add(new NotificationChannel(NotificationChannel.WorkPhoneId, "Work Phone", "6782978987"));

            // Home phone (not enabled).
            channel = new NotificationChannel(NotificationChannel.HomePhoneId, "Home Phone", "7654321");
            channel.IsEnabled = false;
            masterChannels.Add(channel);

            // Other phone (not enabled).
            channel = new NotificationChannel(NotificationChannel.OtherPhoneId, "Other Phone", "9876543");
            channel.IsEnabled = false;
            masterChannels.Add(channel);
            
            if (includeCustomChannels)
            {
                masterChannels.Add(new NotificationCustomChannel(NotificationChannelType.Communicator, "My IM channel", "foo@bar.com"));
            }

            return masterChannels;
        }

        /// <summary>
        /// Helper method to compare a master and reference channel, optionally ignoring the IsEnabled property.
        /// </summary>
        /// <param name="masterChannel">The master channel.</param>
        /// <param name="referenceChannel">Reference channel to compare against.</param>
        /// <param name="expectedIsEnabledValue">Expected value for the referenceChannel.IsEnabled.</param>
        private static bool CompareMasterToReference(NotificationChannel masterChannel, NotificationChannel referenceChannel, bool expectedIsEnabledValue)
        {
            Assert.IsNotNull(masterChannel, "masterChannel");
            Assert.IsNotNull(referenceChannel, "referenceChannel");

            return expectedIsEnabledValue == referenceChannel.IsEnabled &&
                masterChannel.Id == referenceChannel.Id &&
                masterChannel.NotificationChannelType == referenceChannel.NotificationChannelType &&
                masterChannel.UserDefinedName == referenceChannel.UserDefinedName &&
                masterChannel.UserDefinedValue == referenceChannel.UserDefinedValue;
        }
    }
}
